package com.huanrong.util;

import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
 * 该类仅限制在redis内部操作数据
 * 
 * @author njwangchuan
 *         
 */
@Component
public class RedisUtil
{
    
    private static final Logger logger = Logger.getLogger(RedisUtil.class);
    
    private static final long MAXLONG = 9007199254740992L;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    /**
     * 每个线程持有锁超时时间 30s
     */
    private static final long LOCK_EXPIRE = 30;
    
    /**
     * redis锁前缀
     */
    private static final String LOCK_PREFIX = "LOCK:";
    
    /**
     * 
     * @param redisKey
     * @param redisValue
     */
    public void saveItem(String redisKey, String redisValue)
    {
        redisTemplate.opsForValue().set(redisKey, redisValue);
    }
    
    /**
     * 
     * @param redisKey
     * @param redisValue
     * @param unit
     */
    public void saveItem(String redisKey, String redisValue, long timeout, TimeUnit unit)
    {
        redisTemplate.opsForValue().set(redisKey, redisValue, timeout, unit);
    }
    
    /**
     * 
     * @param redisKey
     * @return
     */
    public String getItem(String redisKey)
    {
        return redisTemplate.opsForValue().get(redisKey);
    }
    
    /**
     * 
     * @param keyPattern
     * @return
     */
    public Set<String> keySets(String keyPattern)
    {
        return redisTemplate.keys(keyPattern);
    }
    
    /**
     * 
     * @param hashKey
     * @return
     */
    public JSONArray queryItemAll(String hashKey)
    {
        JSONArray resp = new JSONArray();
        Set<String> redisKeys = redisTemplate.keys(hashKey + ":*");
        for (String node : redisKeys)
        {
            resp.add(JSONObject.fromObject(redisTemplate.opsForValue().get(node)));
        }
        return resp;
    }
    
    /**
     * 
     * @param hashKey
     * @param rangeKey
     */
    public void deleteItem(String hashKey, long rangeKey)
    {
        String redisKey = getRedisKey(hashKey, rangeKey);
        redisTemplate.delete(redisKey);
        logger.info("remove redis key:[" + redisKey + "]");
    }
    
    /**
     * 
     * @param redisKey
     */
    public void deleteItem(String redisKey)
    {
        redisTemplate.delete(redisKey);
        logger.debug("remove redis key:[" + redisKey + "]");
    }
    
    /**
     * 
     * @param hashKey
     */
    public void deleteItemWithHashKey(String hashKey)
    {
        redisTemplate.delete(redisTemplate.keys(hashKey + ":*"));
        logger.info("remove redis key with hashKey:[" + hashKey + "]");
    }
    
    public void deleteItemWithStartKey(String startKey)
    {
        redisTemplate.delete(redisTemplate.keys(startKey + "*"));
        logger.info("remove redis key with hashKey:[" + startKey + "]");
    }
    
    public void expireItem(String redisKey, long timeout, TimeUnit unit)
    {
        redisTemplate.expire(redisKey, timeout, unit);
    }
    
    // SortedSet（有序集合）
    /**
     * 实现命令：ZADD key score member，将一个 member元素及其 score值加入到有序集 key当中。
     * 
     * @param key
     * @param member
     * @param score
     */
    public void ZADD(String key, String member, double score)
    {
        redisTemplate.opsForZSet().add(key, member, score);
    }
    
    /**
     * 实现命令：ZRANGE key start stop，返回有序集 key中，指定区间内的成员。
     * 
     * @param key
     * @param start
     * @param stop
     * @return
     */
    public Set<String> rangeByIdx(String key, int sinceIdx, int maxSize, boolean scanIndexForward)
    {
        if (scanIndexForward)
        {
            return redisTemplate.opsForZSet().range(key, sinceIdx, sinceIdx + maxSize);
        }
        else
        {
            return redisTemplate.opsForZSet().range(key, sinceIdx - maxSize > 0 ? sinceIdx - maxSize : 0, sinceIdx);
        }
    }
    
    /**
     * 实现命令：ZRANGE key start stop，返回有序集 key中，指定区间内的成员。
     * 
     * @param key
     * @param start
     * @param stop
     * @return
     */
    public Set<String> rangeById(String key, long sinceId, int maxSize, boolean scanIndexForward)
    {
        int step = getStep(sinceId);
        if (scanIndexForward)
        {
            return redisTemplate.opsForZSet().rangeByScore(key, sinceId + step, Long.MAX_VALUE, 0, maxSize);
        }
        else
        {
            return redisTemplate.opsForZSet().reverseRangeByScore(key,
                0,
                sinceId > 1 ? (sinceId - step) : Long.MAX_VALUE,
                0,
                maxSize);
        }
    }
    
    public long size(String key)
    {
        return redisTemplate.opsForZSet().size(key);
    }
    
    /**
     * 
     * @param hashKey
     * @param rangeKey
     * @return
     */
    public boolean hasKey(String hashKey, long rangeKey)
    {
        return redisTemplate.hasKey(getRedisKey(hashKey, rangeKey));
    }
    
    /**
     * 
     * @param redisKey
     * @return
     */
    public boolean hasKey(String redisKey)
    {
        return redisTemplate.hasKey(redisKey);
    }
    
    /**
     * 
     * @param redisKey key
     * @param delta 增量
     * @return
     */
    public long incr(String redisKey, long delta)
    {
        return redisTemplate.opsForValue().increment(redisKey, delta);
    }
    
    /**
     * 
     * @param hashKey
     * @param rangeKey
     * @return
     */
    private String getRedisKey(String hashKey, long rangeKey)
    {
        return hashKey + ":" + rangeKey;
    }
    
    private int getStep(long id)
    {
        if (Math.abs(id) <= MAXLONG)
        {
            return 1;
        }
        else
        {
            int step = 1;
            while (Math.abs(id) > MAXLONG * step)
            {
                step *= 10;
            }
            return step;
        }
    }
    
    /**
     * 在指定时间内获取redis锁
     * 
     * @Description
     * @param key
     * @param timeout 超时时间
     * @return
     */
    public boolean lock(String key, long timeout)
    {
        long begin = 0;
        do
        {
            if (lock(key))
            {
                return true;
            }
            begin += 3;
            try
            {
                Thread.sleep(3000);
            }
            catch (InterruptedException e)
            {
                logger.error("获取锁出错", e);
                return false;
            }
        } while (begin <= timeout);
        return false;
    }
    
    /**
     * 获取redis锁
     * 
     * @Description
     * @param key
     * @return
     */
    public boolean lock(String key)
    {
        logger.info("try get lock for:" + key);
        key = LOCK_PREFIX + key;
        RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
        if (redisConnection.setNX(key.getBytes(), key.getBytes()))
        {
            redisTemplate.expire(key, LOCK_EXPIRE, TimeUnit.SECONDS);
            redisConnection.close();
            return true;
        }
        redisConnection.close();
        return false;
    }
    
    /**
     * 释放锁
     * 
     * @Description
     * @param key
     */
    public void unLock(String key)
    {
        key = LOCK_PREFIX + key;
        redisTemplate.delete(key);
    }
}
